home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Tools / Languages / Caml Light 0.61 / Source / src / compiler / back.ml next >
Encoding:
Text File  |  1993-09-24  |  15.1 KB  |  471 lines  |  [TEXT/MPS ]

  1. (*  back.ml : translation of lambda terms to lists of instructions. *)
  2.  
  3. #open "misc";;
  4. #open "const";;
  5. #open "lambda";;
  6. #open "prim";;
  7. #open "instruct";;
  8.  
  9. (* "is_return" determines if we're in tail call position. *)
  10.  
  11. let is_return =
  12.   function Kreturn :: _ -> true | _ -> false
  13. ;;
  14.  
  15. (* Label generation *)
  16.  
  17. let label_counter = ref 0;;
  18.  
  19. let reset_label () =
  20.   label_counter := 0
  21. and new_label () =
  22.   incr label_counter; !label_counter
  23. ;;
  24.  
  25. (* Add a label to a list of instructions. *)
  26.  
  27. let label_code = function
  28.     Kbranch lbl :: _ as C ->
  29.       (lbl, C)
  30.   | Klabel lbl :: _ as C ->
  31.       (lbl, C)
  32.   | C ->
  33.       let lbl = new_label() in
  34.         (lbl, Klabel lbl :: C)
  35. ;;
  36.  
  37. (* Generate a branch to the given list of instructions. *)
  38.  
  39. let make_branch = function
  40.     Kreturn :: _ as C ->
  41.       (Kreturn, C)
  42.   | (Kbranch _ as branch) :: _ as C ->
  43.       (branch, C)
  44.   | C ->
  45.       let lbl = new_label() in
  46.         (Kbranch lbl, Klabel lbl :: C)
  47. ;;
  48.  
  49. (* Discard all instructions up to the next label. *)
  50.  
  51. let rec discard_dead_code = function
  52.     [] -> []
  53.   | Klabel _ :: _ as C -> C
  54.   | _ :: rest -> discard_dead_code rest
  55. ;;
  56.  
  57. (* Generate a jump through table, unless unnecessary. *)
  58.  
  59. let add_switchtable switchtable C =
  60.   try
  61.     for i = 1 to vect_length switchtable - 1 do
  62.       if switchtable.(i) != switchtable.(0) then raise Exit
  63.     done;
  64.     match C with
  65.       Klabel lbl :: C1 ->
  66.         if lbl == switchtable.(0) then C else Kbranch switchtable.(0) :: C
  67.     | _ ->
  68.         Kbranch switchtable.(0) :: C
  69.   with Exit ->
  70.     Kswitch switchtable :: C
  71. ;;
  72.  
  73. (* Compiling N-way integer branches *)
  74.  
  75. (* Input: a list of (key, action) pairs, where keys are integers. *)
  76. (* Output: a decision tree with the format below *)
  77.  
  78. type decision_tree =
  79.     DTfail
  80.   | DTinterval of decision_tree * decision * decision_tree
  81.  
  82. and decision =
  83.   { low: int;
  84.     act: lambda vect;
  85.     high: int }
  86. ;;
  87.  
  88. let compile_nbranch int_of_key casel =
  89.   let casei =
  90.     map (fun (key, act) -> (int_of_key key, act)) casel in
  91.   let cases =
  92.     sort__sort (fun (key1,act1) (key2,act2) -> key1 <= key2) casei in
  93.   let keyv =
  94.     vect_of_list (map fst cases)
  95.   and actv =
  96.     vect_of_list (map snd cases) in
  97.   let n =
  98.     vect_length keyv in
  99.   let extract_act start stop =
  100.     let v =
  101.       make_vect (keyv.(stop) - keyv.(start) + 1) Lstaticfail in
  102.     for i = start to stop do
  103.       v.(keyv.(i) - keyv.(start)) <- actv.(i)
  104.     done;
  105.     v in
  106.   (* Now we partition the set of keys keyv into maximal dense enough segments.
  107.      A segment is dense enough if its span (max point - min point) is
  108.      less than four times its size (number of points). *)
  109.   let rec partition start =
  110.     if start >= n then [] else
  111.     let stop = ref (n-1) in
  112.     while keyv.(!stop) - keyv.(start) >= 255 or
  113.           keyv.(!stop) - keyv.(start) > 4 * (!stop - start) do
  114.       decr stop
  115.     done;
  116.     (* We've found a dense enough segment.
  117.        In the worst case, !stop = start and the segment is a single point *)
  118.     (* Now build the vector of actions *)
  119.     { low = keyv.(start);
  120.       act = extract_act start !stop;
  121.       high = keyv.(!stop) } :: partition (!stop + 1) in
  122.   let part =
  123.     vect_of_list (partition 0) in
  124.   (* We build a balanced binary tree *)
  125.   let rec make_tree start stop =
  126.     if start > stop then
  127.       DTfail
  128.     else
  129.       let middle = (start + stop) / 2 in
  130.         DTinterval(make_tree start (middle-1),
  131.                    part.(middle), 
  132.                    make_tree (middle+1) stop) in
  133.   make_tree 0 (vect_length part - 1)
  134. ;;
  135.  
  136. (* To check if a switch construct contains tags that are unknown at
  137.    compile-time (i.e. exception tags). *)
  138.  
  139. let switch_contains_extensibles casel =
  140.   exists (function ConstrExtensible _, _ -> true | _ -> false) casel
  141. ;;
  142.  
  143. (* Inversion of a boolean test ( < becomes >= and so on) *)
  144.  
  145. let invert_bool_test =
  146.   let invert_prim_test = function
  147.       PTeq -> PTnoteq
  148.     | PTnoteq -> PTeq
  149.     | PTnoteqimm x -> fatal_error "invert_prim_test"
  150.     | PTlt -> PTge
  151.     | PTle -> PTgt
  152.     | PTgt -> PTle
  153.     | PTge -> PTlt in
  154.   function
  155.       Peq_test -> Pnoteq_test
  156.     | Pnoteq_test -> Peq_test
  157.     | Pint_test t -> Pint_test(invert_prim_test t)
  158.     | Pfloat_test t -> Pfloat_test(invert_prim_test t)
  159.     | Pstring_test t -> Pstring_test(invert_prim_test t)
  160.     | Pnoteqtag_test t -> fatal_error "invert_prim_test2"
  161. ;;
  162.  
  163. (* Production of an immediate test *)
  164.  
  165. let test_for_atom = function
  166.     ACint x -> Pint_test(PTnoteqimm x)
  167.   | ACchar x -> Pint_test(PTnoteqimm (int_of_char x))
  168.   | ACfloat x -> Pfloat_test(PTnoteqimm x)
  169.   | ACstring x -> Pstring_test(PTnoteqimm x)
  170. ;;
  171.  
  172. (* To keep track of function bodies that remain to be compiled. *)
  173.  
  174. let still_to_compile  = (stack__new () : (lambda * int) stack__t);;
  175.  
  176. (* The translator from lambda terms to lists of instructions.
  177.  
  178.    staticfail : the label where Lstaticfail must branch.
  179.    lambda : the lambda term to compile.
  180.    C : the continuation, i.e. the code that follows the code for lambda.
  181.  
  182.    The tests on the continuation detect tail-calls and avoid jumps to jumps,
  183.    or jumps to function returns.
  184.  
  185. *)
  186.  
  187. let rec compile_expr staticfail =
  188.   let rec compexp expr C = match expr with
  189.     Lvar n ->
  190.         Kaccess n :: C
  191.   | Lconst cst ->
  192.        (match C with
  193.           (Kquote _ | Kget_global _ | Kaccess _ | Kpushmark) :: _ -> C
  194.         | _ -> Kquote cst :: C)
  195.   | Lapply(body, args) ->
  196.        (match C with
  197.           Kreturn :: C' ->
  198.             compexplist args (Kpush :: compexp body (Ktermapply :: C'))
  199.         | _ ->
  200.             Kpushmark ::
  201.             compexplist args (Kpush :: compexp body (Kapply :: C)))
  202.   | Lfunction body ->
  203.         if is_return C then
  204.           Kgrab :: compexp body C
  205.         else begin
  206.           let lbl = new_label() in
  207.             stack__push (body, lbl) still_to_compile;
  208.             Kclosure lbl :: C
  209.           end
  210.   | Llet(args, body) ->
  211.         let C1 = if is_return C then C else Kendlet(list_length args) :: C in
  212.         let rec comp_args = function
  213.             [] ->
  214.               compexp body C1
  215.       | exp::rest ->
  216.               compexp exp (Klet :: comp_args rest) in
  217.         comp_args args
  218.   | Lletrec([Lfunction f, _], body) ->
  219.         let C1 = if is_return C then C else Kendlet 1 :: C in
  220.         let lbl = new_label() in
  221.           stack__push (f, lbl) still_to_compile;
  222.           Kletrec1 lbl :: compexp body C1
  223.   | Lletrec(args, body) ->
  224.         let size = list_length args in
  225.         let C1 = if is_return C then C else Kendlet size :: C in
  226.     let rec comp_args i = function
  227.         [] ->
  228.               compexp body C1
  229.       | (exp, sz)::rest ->
  230.               compexp exp (Kpush :: Kaccess i :: Kprim Pupdate ::
  231.                             comp_args (i-1) rest) in
  232.         list_it
  233.           (fun (e, sz) C -> Kprim(Pdummy sz) :: Klet :: C)
  234.           args (comp_args (size-1) args)
  235.   | Lprim(Pget_global qualid, []) ->
  236.         Kget_global qualid :: C
  237.   | Lprim(Pset_global qualid, [exp]) ->
  238.         compexp exp (Kset_global qualid :: C)
  239.   | Lprim(Pmakeblock tag, explist) ->
  240.         compexplist explist (Kmakeblock(tag, list_length explist) :: C)
  241.   | Lprim(Pnot, [exp]) ->
  242.        (match C with
  243.           Kbranchif lbl :: C' ->
  244.             compexp exp (Kbranchifnot lbl :: C')
  245.         | Kbranchifnot lbl :: C' ->
  246.             compexp exp (Kbranchif lbl :: C')
  247.         | _ ->
  248.             compexp exp (Kprim Pnot :: C))
  249.   | Lprim((Ptest tst as p), explist) ->
  250.        (match C with
  251.           Kbranchif lbl :: C' ->
  252.             compexplist explist (Ktest(tst,lbl) :: C')
  253.         | Kbranchifnot lbl :: C' ->
  254.             compexplist explist (Ktest(invert_bool_test tst,lbl) :: C')
  255.         | _ ->
  256.             compexplist explist (Kprim p :: C))
  257.   | Lprim(Praise, explist) ->
  258.         compexplist explist (Kprim Praise :: discard_dead_code C)
  259.   | Lprim(p, explist) ->
  260.         compexplist explist (Kprim p :: C)
  261.   | Lstatichandle(body, Lstaticfail) ->
  262.         compexp body C
  263.   | Lstatichandle(body, handler) ->
  264.         let branch1, C1 = make_branch C
  265.         and lbl2 = new_label() in
  266.           compile_expr lbl2 body (branch1 :: Klabel lbl2 :: compexp handler C1)
  267.   | Lstaticfail ->
  268.         Kbranch staticfail :: discard_dead_code C
  269.   | Lhandle(body, handler) ->
  270.         let branch1, C1 = make_branch C in
  271.         let lbl2 = new_label() in
  272.         let C2 = if is_return C1 then C1 else Kendlet 1 :: C1 in
  273.           Kpushtrap lbl2 ::
  274.             compexp body
  275.                     (Kpoptrap :: branch1 :: Klabel lbl2 :: compexp handler C2)
  276.   | Lifthenelse(cond, ifso, ifnot) ->
  277.         comp_test2 cond ifso ifnot C
  278.   | Lsequence(exp1, exp2) ->
  279.         compexp    exp1 (compexp exp2 C)
  280.   | Lwhile(cond, body) ->
  281.         let lbl1 = new_label() and lbl2 = new_label() in
  282.           Kbranch lbl1 :: Klabel lbl2 :: Kcheck_signals ::
  283.           compexp body (Klabel lbl1 :: compexp cond (
  284.             Kbranchif lbl2 :: Kquote(const_unit) :: C))
  285.   | Lfor(start, stop, up_flag, body) ->
  286.         let lbl_end = new_label()
  287.         and lbl_loop = new_label() in
  288.           compexp start (
  289.             Kmakeblock(ConstrRegular(0,1), 1) :: Klet ::
  290.             compexp stop (
  291.               Klet :: Klabel lbl_loop :: Kcheck_signals ::
  292.               Kaccess 1 :: Kprim(Pfield 0) :: Kpush :: Kaccess 0 ::
  293.               Ktest(Pint_test(if up_flag then PTlt else PTgt), lbl_end) ::
  294.               compexp body (
  295.                 Kaccess 1 :: Kprim(if up_flag then Pincr else Pdecr) ::
  296.                 Kbranch lbl_loop ::
  297.                 Klabel lbl_end :: Kendlet 2 ::
  298.                 Kquote(const_unit) :: C)))
  299.   | Lsequand(exp1, exp2) ->
  300.        (match C with
  301.           Kbranch lbl :: _  ->
  302.             compexp exp1 (Kstrictbranchifnot lbl :: compexp exp2 C)
  303.         | Kbranchifnot lbl :: _ ->
  304.             compexp exp1 (Kbranchifnot lbl :: compexp exp2 C)
  305.         | Kbranchif lbl :: C' ->
  306.             let lbl1, C1 = label_code C' in
  307.               compexp exp1 (Kbranchifnot lbl1 ::
  308.                             compexp exp2 (Kbranchif lbl :: C1))
  309.         | _ ->
  310.             let lbl = new_label() in
  311.               compexp exp1 (Kstrictbranchifnot lbl ::
  312.                             compexp exp2 (Klabel lbl :: C)))
  313.   | Lsequor(exp1, exp2) ->
  314.        (match C with
  315.           Kbranch lbl :: _  ->
  316.             compexp exp1 (Kstrictbranchif lbl :: compexp exp2 C)
  317.         | Kbranchif lbl :: _  ->
  318.             compexp exp1 (Kbranchif lbl :: compexp exp2 C)
  319.         | Kbranchifnot lbl :: C' ->
  320.             let lbl1, C1 = label_code C' in
  321.               compexp exp1 (Kbranchif lbl1 ::
  322.                             compexp exp2 (Kbranchifnot lbl :: C1))
  323.         | _ ->
  324.             let lbl = new_label() in
  325.               compexp exp1 (Kstrictbranchif lbl ::
  326.                             compexp exp2 (Klabel lbl :: C)))
  327.  
  328.   | Lcond(arg, casel) ->
  329.         let C1 =
  330.           if match casel with
  331.             (ACint _, _) :: _ -> true
  332.           | (ACchar _, _) :: _ -> true
  333.           | _ -> false
  334.           then
  335.             comp_decision (compile_nbranch int_of_atom casel) C
  336.           else
  337.             comp_tests (map (fun (cst,act) -> (test_for_atom cst, act)) casel) C
  338.         in
  339.           compexp arg C1
  340.  
  341.   | Lswitch(1, arg, [ConstrRegular(_,_), exp]) ->
  342.         compexp exp C
  343.         (* En supposant que l'argument est toujours du code pur !!!
  344.            (vrai quand c'est le pattern-matcher qui genere le Switch).     *)
  345.   | Lswitch(2, arg, [ConstrRegular(0,_), exp0]) ->
  346.         compexp arg (Kbranchif staticfail :: compexp exp0 C)
  347.   | Lswitch(2, arg, [ConstrRegular(1,_), exp1]) ->
  348.         compexp arg (Kbranchifnot staticfail :: compexp exp1 C)
  349.   | Lswitch(2, arg, [ConstrRegular(0,_), exp0; ConstrRegular(1,_), exp1]) ->
  350.         comp_test2 arg exp1 exp0 C
  351.   | Lswitch(2, arg, [ConstrRegular(1,_), exp1; ConstrRegular(0,_), exp0]) ->
  352.         comp_test2 arg exp1 exp0 C
  353.   | Lswitch(size, arg, casel) ->
  354.         let C1 =
  355.           if switch_contains_extensibles casel then
  356.             comp_tests
  357.               (map (fun (tag,act) -> (Pnoteqtag_test tag, act)) casel) C
  358.           else if list_length casel >= size - 5 then
  359.             Kprim Ptag_of :: comp_direct_switch size casel C
  360.           else
  361.             Kprim Ptag_of ::
  362.               comp_decision (compile_nbranch int_of_constr_tag casel) C
  363.        in
  364.          compexp arg C1
  365.   | Lshared(expr, lbl_ref) ->
  366.        if !lbl_ref == Nolabel then begin
  367.          let lbl = new_label() in
  368.            lbl_ref := lbl;
  369.            Klabel lbl :: compexp expr C
  370.        end else begin
  371.          Kbranch !lbl_ref :: discard_dead_code C
  372.        end
  373.  
  374.   and compexplist = fun
  375.       [] C -> C
  376.     | [exp] C -> compexp exp C
  377.     | (exp::rest) C -> compexplist rest (Kpush :: compexp exp C)
  378.  
  379.   and comp_test2 cond ifso ifnot C =
  380.     let branch1, C1 = make_branch C
  381.     and lbl2 = new_label() in
  382.       compexp cond (Kbranchifnot lbl2 ::
  383.                    compexp ifso (branch1 :: Klabel lbl2 :: compexp ifnot C1))
  384.  
  385.   and comp_tests casel C =
  386.     let branch1, C1 =
  387.       make_branch C in
  388.     let rec comp = function
  389.         [] ->
  390.           fatal_error "comp_tests"
  391.       | [test,exp] ->
  392.           Ktest(test, staticfail) :: compexp exp C1
  393.       | (test,exp)::rest ->
  394.           let lbl = new_label() in
  395.             Ktest(test, lbl) :: compexp exp (branch1 :: Klabel lbl :: comp rest)
  396.     in comp casel
  397.  
  398.   and comp_switch v branch1 C =
  399.       let switchtable =
  400.         make_vect (vect_length v) staticfail in
  401.       let rec comp_cases n =
  402.         if n >= vect_length v then
  403.           C
  404.         else begin
  405.           let (lbl, C1) =
  406.             label_code (compexp v.(n) (branch1 :: comp_cases (n+1))) in
  407.           switchtable.(n) <- lbl;
  408.           C1
  409.         end in
  410.       add_switchtable switchtable (discard_dead_code(comp_cases 0))
  411.  
  412.   and comp_decision tree C =
  413.     let branch1, C1 = make_branch C in
  414.     let rec comp_dec = fun
  415.       (DTfail) C ->
  416.         Kbranch staticfail :: discard_dead_code C
  417.     | (DTinterval(left, dec, right)) C ->
  418.         let (lbl_right, Cright) =
  419.           match right with
  420.             DTfail -> (staticfail, C)
  421.           | _      -> label_code (comp_dec right C) in
  422.         let (lbl_left, Cleft) =
  423.           match left with
  424.             DTfail -> (staticfail, Cright)
  425.           | _ ->      label_code (comp_dec left Cright) in
  426.         Kbranchinterval(dec.low, dec.high, lbl_left, lbl_right) ::
  427.         begin match vect_length dec.act with
  428.                 1 -> compexp dec.act.(0) (branch1 :: Cleft)
  429.               | _ -> comp_switch dec.act branch1 Cleft
  430.         end in
  431.     comp_dec tree C1
  432.  
  433.   and comp_direct_switch size casel C =
  434.     let branch1, C1 = make_branch C in
  435.     let switchtable = make_vect size staticfail in
  436.     let rec comp_case = function
  437.         [] ->
  438.           fatal_error "comp_switch"
  439.       | [tag, exp] ->
  440.           let (lbl, C2) = label_code (compexp exp C1) in
  441.           switchtable.(int_of_constr_tag tag) <- lbl;
  442.           C2
  443.       | (tag, exp) :: rest ->
  444.           let (lbl, C2) =
  445.             label_code (compexp exp (branch1 :: comp_case rest)) in
  446.           switchtable.(int_of_constr_tag tag) <- lbl;
  447.           C2
  448.     in
  449.       add_switchtable switchtable (discard_dead_code(comp_case casel))
  450.  
  451.   in compexp
  452. ;;
  453.  
  454. let rec compile_rest C =
  455.   try
  456.     let (exp, lbl) = stack__pop still_to_compile in
  457.       compile_rest (Klabel lbl :: compile_expr Nolabel exp (Kreturn :: C))
  458.   with stack__Empty ->
  459.     C
  460. ;;
  461.  
  462. let compile_lambda (rec_flag : bool) expr =
  463.   stack__clear still_to_compile;
  464.   reset_label();
  465.   let init_code =
  466.     compile_expr Nolabel expr [] in
  467.   let function_code =
  468.     compile_rest [] in
  469.   { kph_rec = rec_flag; kph_init = init_code; kph_fcts = function_code }
  470. ;;
  471.